import re
import subprocess
from weiqi.dlgo.gotypes import Player
from weiqi.dlgo.score import ComputeGameResult
from weiqi.dlgo.utils import PrintBoard
from ..agent import TerminationAgent, LoadPredictionAgent
from ..board import GameState, Move
from ...utils import R, GtpPositionToMove, MoveToGtpPosition

class LocalGtpBot:
    def __init__(self, go_bot, termination=None, handicap=0, 
                 opponent='gnugo', output_sgf='out.sgf', our_color='b') -> None:
        self.bot = TerminationAgent(go_bot, termination)
        self.handicap = handicap
        self.stopped = False
        self.game_state = GameState.NewGame(R.BoardSize)
        self.sgf = SGFWriter(output_sgf)
        self.our_color:Player = Player.black if our_color == 'b' else Player.white
        self.their_color = self.our_color.Other

        cmd = self.OpponentCommand(opponent)
        pipe = subprocess.PIPE
        self.gtp_stream = subprocess.Popen(cmd, stdin=pipe, stdout=pipe, bufsize=0)

    @staticmethod
    def OpponentCommand(opponent):
        if opponent == 'gnugo':
            return ['gnugo', '--mode', 'gtp']
        elif opponent == 'pachi':
            return ['pachi']
        else:
            raise ValueError(f'Unkown bot name {opponent}')
        
    def SendCommand(self, cmd):
        self.gtp_stream.stdin.write(cmd.encode('utf-8'))

    def GetResponse(self):
        successed = False
        result = ''
        while not successed:
            line = self.gtp_stream.stdout.readline().decode('utf-8')
            if line[0] == '=':
                successed = True
                line = line.strip()
                result = re.sub('^= ?', '', line)
        return result
    
    def CommandAndResponse(self, cmd):
        self.SendCommand(cmd)
        return self.GetResponse()
    
    def Run(self):
        self.CommandAndResponse('boardsize 19\n')
        self.SetHandicap()
        self.Play()
        self.sgf.WriteSgf()

    def SetHandicap(self):
        if self.handicap == 0:
            self.CommandAndResponse('komi 7.5\n')
            self.sgf.Append('KM[7.5]\n')
        else:
            stones = self.CommandAndResponse(f'fixed_handicap {self.handicap}\n')
            sgf_handicap = f'HA[{self.handicap}]AB'
            for p in stones.split(' '):
                move = GtpPositionToMove(p)
                self.game_state = self.game_state.ApplyMove(move)
                sgf_handicap = sgf_handicap + '[' + self.sgf.Coordinates(move) + ']'
            self.sgf.Append(sgf_handicap + '\n')
    
    def Play(self):
        while not self.stopped:
            if self.game_state.next_player == self.our_color:
                self.PlayOurMove()
            else:
                self.PlayTheirMove()
            PrintBoard(self.game_state.board)
            print('Estimated result: ')
            print(ComputeGameResult(self.game_state))

    def PlayOurMove(self):
        move = self.bot.SelectMove(self.game_state)
        self.game_state = self.game_state.ApplyMove(move)
        our_name = self.our_color.name
        our_letter = our_name[0].upper()
        sgf_move = ''
        if move.is_pass:
            self.CommandAndResponse(f'play {our_name} pass\n')
        elif move.is_resign:
            self.CommandAndResponse(f'play {our_name} resign\n')
        else:
            pos = MoveToGtpPosition(move)
            self.CommandAndResponse(f'play {our_name} {pos}\n')
            sgf_move = self.sgf.Coordinates(move)
        self.sgf.Append(f';{our_letter}[{sgf_move}]\n')

    def PlayTheirMove(self):
        their_name = self.their_color.name
        their_letter = their_name[0].upper()
        pos = self.CommandAndResponse(f'genmove {their_name}\n')
        if pos.lower() == 'resign':
            self.game_state = self.game_state.ApplyMove(Move.Resign())
            self.stopped = True
        elif pos.lower() == 'pass':
            self.game_state = self.game_state.ApplyMove(Move.Pass())
            self.sgf.Append(f';{their_letter}[]')
            if self.game_state.last_move.is_pass:
                self.stopped = True 
        else:
            move = GtpPositionToMove(pos)
            self.game_state = self.game_state.ApplyMove(move)
            self.sgf.Append(f';{their_letter}[{self.sgf.Coordinates(move)}]\n')

class SGFWriter:
    def __init__(self, output_sgf) -> None:
        self.output_sgf = output_sgf
        self.letters = 'abcdefghjklmnopqrst'
        self.sgf =  "(;GM[1]FF[4]CA[UTF-8]SZ[19]RU[Chinese]\n"

    def Append(self, text):
        self.sgf = self.sgf + text 

    def WriteSgf(self):
        self.sgf = self.sgf + ')\n'
        with open(self.output_sgf, 'w') as f:
            f.write(self.sgf)

    def Coordinates(self, move:Move):
        point = move.point
        return self.letters[point.col-1] + self.letters[point.row-1]
    

def run_local():
    from os.path import dirname, abspath,join
    import h5py
    from ..agent import PassWhenOpponentPassesStrategy

    BotDir = dirname(dirname(dirname(dirname(dirname(abspath(__file__))))))
    BotPath = join(BotDir, 'deep_bot.h5')
    print(BotPath)
    bot = LoadPredictionAgent(h5py.File(BotPath, 'r'))
    gnugo = LocalGtpBot(go_bot=bot, termination=PassWhenOpponentPassesStrategy(),
                        handicap=0, opponent='gnugo')
    gnugo.Run()

if __name__ == '__main__':
    pass 